/**CFile****************************************************************
 
  FileName    [app.c] 

  SystemName  []

  PackageName []
  
  Synopsis    [基于ABC的近似计算函数编写]

  Author      []
   
  Affiliation []

  Date        [2021.7.13]

***********************************************************************/
#include <stdio.h>
#include <time.h>
#include "base/abc/abc.h"
#include "opt/cut/cut.h"
#include "opt/cut/cutInt.h"
#include "bool/dec/dec.h"
#include "bool/kit/kit.h"
#include "app.h"
#include "aig/aig/aig.h"
#include "miter.h"
#include "countmin.h"
#include "opt.h"
static int count = 0;   //记录在saveMffc中被MffcCone引用的次数，作为结构体中MffcArray所存储的Mffc切割集的个数。
static Abc_Ntk_t *Ntk_Copy(Abc_Ntk_t *pNtk1);
extern void AppManager_initial(AppManager *dMan, Abc_Ntk_t *pNtk);
void AppNodeMffcCone( Abc_Obj_t * pNode, AppManager *dMan);
void AppSaveMffc( Abc_Ntk_t * pNtk,  AppManager *dMan);
void AppReplaceMffc(Abc_Ntk_t * pNtk, Abc_Obj_t * pObj,AppManager *dMan,int module);
extern void App_Free(AppManager *dMan) ;
static void free_MffcArray(AppManager *dMan);
float  AppErrorcompute(Abc_Ntk_t* pNtk,Abc_Ntk_t* pNtk_app);
void   AppNtksplit(Abc_Ntk_t* pNtk, Abc_Obj_t * pNode, int fanoutnum);
Abc_Obj_t * AppNtkCreateObj( Abc_Ntk_t * pNtk, Abc_ObjType_t Type );
void Apppreorder(Abc_Ntk_t*pNtk,Abc_Obj_t *pCopy,Abc_Obj_t* pNode);
int traceNodetoPo(Abc_Ntk_t * pNtk,Abc_Obj_t *pNode);
int GetAverageLevel(Abc_Ntk_t * pNtk, int level);
Abc_Ntk_t* GetBestNtk(Abc_Ntk_t * pNtk,Abc_Ntk_t * pNtk_org, int average,int j,double Lastrate);
double GetErrorRate(Abc_Ntk_t * pNtk_app,Abc_Ntk_t * pNtk_org);
int ReplaceeModule(Abc_Ntk_t * pNtk,Abc_Obj_t *pNode,int fcompile);
/*------------------------------------------------------------------------*/
/* 近似电路替换主程序                                                     */
/*------------------------------------------------------------------------*/
extern Abc_Ntk_t*  App_Substitution(Abc_Ntk_t *pNtk, AppManager ** dMan)
{
  Abc_Obj_t* pNode = NULL;
  Abc_Ntk_t* pNtk_app = pNtk, * pNtk_org = NULL,*ptemp = NULL;
  int i = 0, j = 1,average = 0,fanoutcount = 0,NtkNode = 0,tempNode = 0,templevel = 0;
  double rate = 0,Lastrate =0,finalrate = 0;
	*dMan = (AppManager *)calloc(sizeof(AppManager),sizeof(AppManager));
  //初始化
	AppManager_initial(*dMan, pNtk);
  //保存电路Mffc信息
  AppSaveMffc(pNtk,*dMan);
  //打印基于MFFC分割的节点信息
/*    for(i=0; i<count;i++)
  {
    for (int j=0; j<(*dMan)->MffcCone[i];j++)
    {
     printf("%d ",(*dMan)->MffcArray[i][j]);
    }
     printf("\n");
  }
  printf("\n");  */
  //复制原始电路
  pNtk_org = Ntk_Copy(pNtk);
  //对多扇出节点进行电路拆分
  while(rate<0.2){
/*  因为该算法会选出错误率约束下的最优解，最后一次优化一定会使得该次优化超过错误率
    也就是我们上一次获得的错误率就是我们面积最优近似下对于的最终错误率 */
     finalrate = Lastrate;
     tempNode = Abc_NtkNodeNum(pNtk);
     templevel = Abc_NtkLevel(pNtk);
    //获取当前level下的多扇出节点到PO的平均距离
    if(Abc_NtkNodeNum(pNtk)<300)
    average = 0;
    else
    average = GetAverageLevel(pNtk,j);
    printf("average : %d \n",average);
    printf("level: %d\n",j);
    NtkNode = Abc_NtkNodeNum(pNtk);
    printf("node: %d\n",NtkNode);
    //ptemp用来保存之前一次近似的最优电路，如果下一次近似结果错误率大于设定值，则还原近似电路为ptemp
    ptemp = pNtk_app;
    //获取当前level下最优优化电路结构
    pNtk_app = GetBestNtk(pNtk,pNtk_org,average,j,Lastrate);
    printf("node: %d\n",Abc_NtkNodeNum(pNtk_app));
    //获得当前近似优化的错误率
    Lastrate = GetErrorRate(pNtk_app,pNtk_org);
    printf("Lastrate: %lf\n",Lastrate);
    if(Lastrate>0.2)
    {
        pNtk_app = ptemp;
        break;
    }
    //NtkNode表示近似之前电路节点个数，若近似后节点个数和近似前相同表示当前level下没有可以继续近似的点了，level增加一级
    if(Abc_NtkNodeNum(pNtk_org)>10000&&templevel==Abc_NtkLevel(pNtk_app))
    {
      j++;
    }
    if(average<2&&Abc_NtkNodeNum(pNtk)>300)
    {
        j++;
    }
    if(Abc_NtkNodeNum(pNtk_org)>10000)
        j++;
    else if(NtkNode==Abc_NtkNodeNum(pNtk_app))
        j++;
        
    //判断当前电路层级下所有的节点连接的节点是否都是PO，若是，则退出循环
    if(average==0&&Abc_NtkNodeNum(pNtk)>300)
      break;
    //更新当前电路
    pNtk = pNtk_app;
    //判断当前电路是否还存在多扇出节点，若不存在则退出循环
    Abc_NtkForEachNode1( pNtk_app, pNode, i )
   {
      if(Abc_ObjFanoutNum(pNode) > 1){
          fanoutcount++;
      }
   }
  // printf("fanoutcount: %d\n",fanoutcount);
   if(fanoutcount==0)
      j++;
   else
     fanoutcount = 0;
   if(j==Abc_NtkLevel(pNtk_app))
        break;
  }
  printf("final result:\n");
  Abc_NtkPrintStats( pNtk_app, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 );
  printf("final rate : %lf\n",finalrate);
  Abc_NtkDelete(pNtk_org);
  free_MffcArray(*dMan);
  App_Free(*dMan); 
  return pNtk_app;
}

/*------------------------------------------------------------------------*/
/* 初始化                                                                 */
/*------------------------------------------------------------------------*/
extern void AppManager_initial(AppManager *dMan, Abc_Ntk_t *pNtk)
{
  int nNodes = 0;
  nNodes = Abc_NtkObjNum( pNtk );   //读取电路网络有多少个节点从而开辟对应的空间
  dMan->nNodes = nNodes;
  dMan->MffcArray = (int **)calloc(sizeof(int)*nNodes,sizeof(int));
  dMan->MffcCone = (int *)calloc(sizeof(int)*nNodes,sizeof(int));
}


/*------------------------------------------------------------------------*/
/* 复制网络程序                                                           */
/*------------------------------------------------------------------------*/
static Abc_Ntk_t *Ntk_Copy(Abc_Ntk_t *pNtk1)
{
	Abc_Ntk_t *pNtk_Copy = NULL;
	Abc_Obj_t *pObj = NULL;
	int i = 0;
	pNtk_Copy = Abc_NtkDup(pNtk1);
	pNtk1->pCopy = NULL;
	Abc_NtkForEachObj( pNtk1, pObj, i ) {
		pObj->pCopy = NULL;
	}
	return pNtk_Copy;
}
/*------------------------------------------------------------------------*/
/* 获取所输入电路结构的MFFC节点                                          */
/*------------------------------------------------------------------------*/
void AppSaveMffc( Abc_Ntk_t * pNtk,AppManager *dMan)
{
    Abc_Obj_t * pNode;
    int i;
    Abc_NtkForEachNode( pNtk, pNode, i ){
        if ( Abc_ObjFanoutNum(pNode) > 1 )
        AppNodeMffcCone( pNode, dMan); 
        }
    printf("Mffc切割集个数:%d\n",count);
}

void AppNodeMffcCone( Abc_Obj_t * pNode, AppManager *dMan)
{
    Vec_Ptr_t * vCone, * vSupp;
    Abc_Obj_t * pObj;
    int i=0;
    vCone = Vec_PtrAlloc( 100 );
    vSupp = Vec_PtrAlloc( 100 );
    Abc_NodeDeref_rec( pNode );
    Abc_NodeMffcConeSupp( pNode, vCone, vSupp );
    Abc_NodeRef_rec( pNode );
    //为MffcNodes申请长度为vCone大小的空间并清零
    dMan->MffcNodes = (int *)calloc(sizeof(int)*(vCone->nSize),sizeof(int));
    //获取Mffc切割集的根节点id
    dMan->MffcCone[count] = vCone->nSize;
    //将所获取的根节点所包含的子节点id记录到MffcNodes
    Vec_PtrForEachEntry( Abc_Obj_t *, vCone, pObj, i )
    {
      dMan->MffcNodes[i] = Abc_ObjId(pObj);
    }
    //将MffcNodes的值保存到MffcArray中 
    dMan->MffcArray[count] = dMan->MffcNodes;
    count++;
    Vec_PtrFree( vCone );
    Vec_PtrFree( vSupp );
}

/*------------------------------------------------------------------------*/
/* 对多扇出节点拆分为对应扇出个数的子电路                                    */
/*------------------------------------------------------------------------*/
void  AppNtksplit(Abc_Ntk_t* pNtk, Abc_Obj_t * pNode, int fanoutnum)
{
 Abc_Obj_t* pConst1 = NULL,*pConst0 = NULL;
 Abc_Obj_t* pRootfanout;
 int i = 0,module = 0;
 if(fanoutnum>100)
    return;
 //执行到i=fanoutnum-1后，剩下两个扇出，该次循环会对该节点再次进行常量替换，从而只剩下一个扇出
 for(i = 1;i<fanoutnum;i++)
 {
    pRootfanout =Abc_ObjFanout(pNode,fanoutnum-i);
    if(Abc_ObjIsPi(pRootfanout))
        break;
    //判断pRootfanout节点的扇入pNode是fanin0还是fanin1
    if(pNode->Id==Abc_ObjFanin0(pRootfanout)->Id)
    {
      //判断该节点的连线方式如何，fCompl=0为实线连接，fComple=1为虚线连接
      if(pRootfanout->fCompl0==0)
          module = 0;
      else
          module = 1;
    }
    if(pNode->Id==Abc_ObjFanin1(pRootfanout)->Id)
    {

      if(pRootfanout->fCompl1==0)
          module = 0;
      else
          module = 1;
    }
  // printf("fanoutnum: %d\n",pNode->vFanouts.nSize);
   // Apppreorder(pNtk,pCopy,pNode);
    pConst1 = Abc_AigConst1(pNtk);
    pConst0=  Abc_ObjNot(pConst1);
    module = ReplaceeModule(pNtk,pRootfanout,module); 
    if(module)
    Abc_ObjPatchFanin(pRootfanout,pNode,pConst1);
    else
    Abc_ObjPatchFanin(pRootfanout,pNode,pConst0);
    // Apppreorder(pNtk,pConst1,pNode);
 }
} 

/*------------------------------------------------------------------------*/
/* 通过递归获取复制电路结构并对输入PI置0                                     */
/*------------------------------------------------------------------------*/
void Apppreorder(Abc_Ntk_t*pNtk,Abc_Obj_t *pCopy,Abc_Obj_t* pNode)
{
  Abc_Obj_t *ptemp = NULL, *ptempNot = NULL;
    //判断当前节点的fanin0是否为PI，若是则将当前复制的节点与该节点的PI相连；
    //若不是则建立复制节点与下一个节点之间的连接关系并进行递归操作。
    if(!Abc_ObjIsPi(Abc_ObjFanin0(pNode))){
        pCopy->fCompl0 = pNode->fCompl0;
        pCopy->left = AppNtkCreateObj(pNtk,ABC_OBJ_NODE);
        Abc_ObjAddFanin(pCopy,pCopy->left);
        pNode = Abc_ObjFanin0(pNode);
        Apppreorder(pNtk,pCopy->left,pNode);
        
    }
    else{
        pCopy->fCompl0 = pNode->fCompl0;
        ptemp = Abc_AigConst1(pNtk);
        ptempNot = Abc_ObjNot(ptemp);
        Abc_ObjAddFanin(pCopy,ptempNot);
    }
    //同上
    if(!Abc_ObjIsPi(Abc_ObjFanin1(pNode))){
        pCopy->fCompl1 = pNode->fCompl1;
        pCopy->right = AppNtkCreateObj(pNtk,ABC_OBJ_NODE);
        Abc_ObjAddFanin(pCopy,pCopy->right);
        pNode = Abc_ObjFanin1(pNode);
        Apppreorder(pNtk,pCopy->right,pNode);
    }
    else{
        pCopy->fCompl1 = pNode->fCompl1;
        ptemp = Abc_AigConst1(pNtk);
        ptempNot = Abc_ObjNot(ptemp);
        Abc_ObjAddFanin(pCopy,ptempNot);
    }

}
/*------------------------------------------------------------------------*/
/* 追踪节点到PO的最长路径                                                 */
/*------------------------------------------------------------------------*/
int traceNodetoPo(Abc_Ntk_t * pNtk,Abc_Obj_t *pNode)
{
  Abc_Obj_t* ptemp;
  int fanoutSize = 0, num = 0, i = 0,j = 0,fanoutlevel = 0,Id = 0;
  //判断节点是否与PO相连的，若是返回0表示该节点为连接PO的节点
  if(pNode->Level == Abc_NtkLevel(pNtk))
      return 0;
  //通过for循环来寻找节点到PO的最短距离
  for(j = pNode->Level;j < Abc_NtkLevel(pNtk);j++){
    fanoutlevel = pNode->Level;
    fanoutSize = pNode->vFanouts.nSize;
    //printf("%d %d ",fanoutSize,pNode->Id);
      //获取当前节点的扇出个数进行for循环，找到level最大的节点作为下一次循环的节点
      for(i = 0;i < fanoutSize;i++)
      {
        //判断当前节点的扇出是否连接到PO，若与PO相连，则直接返回根节点到当前节点的距离num
        if(Abc_ObjIsPo(Abc_ObjFanout(pNode,i)))
        return num;
        ptemp = Abc_ObjFanout(pNode,i);
        if(ptemp->Level > fanoutlevel)
        {
            fanoutlevel = ptemp->Level;
            Id = ptemp->Id;
        }
      }
      num++;
    pNode = Abc_NtkObj(pNtk,Id);
   // printf("%d ",pNode->Id);
    if(pNode->Level == Abc_NtkLevel(pNtk))
        break;
  }
 // printf("\n");
  return num;
}

/*------------------------------------------------------------------------*/
/* 根据所给扇出节点来决定是用0还是1来进行常量替换                    */
/*------------------------------------------------------------------------*/
int ReplaceeModule(Abc_Ntk_t * pNtk,Abc_Obj_t *pNode,int fcompile)
{
  Abc_Obj_t* ptemp = NULL,*minlevelNode = NULL;
  int fanoutSize = 0,i = 0,j = 0,fanoutlevel = 0,Id = 0,count0 = 0,count1 = 0,module = fcompile;
  //判断节点是否与PO相连
  if(pNode->Level == Abc_NtkLevel(pNtk))
      return 0;
  for(j = pNode->Level;j < Abc_NtkLevel(pNtk);j++)
  {
    fanoutlevel = pNode->Level;
    fanoutSize = pNode->vFanouts.nSize;
      for(i = 0;i < fanoutSize;i++)
      {
        //判断当前节点的扇出是否连接到PO
          if(Abc_ObjIsPo(Abc_ObjFanout(pNode,i)))
            break;
          ptemp = Abc_ObjFanout(pNode,i);
          if(ptemp->Level > fanoutlevel)
          {
              fanoutlevel = ptemp->Level;
              Id = ptemp->Id;
          }
          if(pNode->Level == Abc_NtkLevel(pNtk))
            break;
          }
    minlevelNode = Abc_NtkObj(pNtk,Id);
  if(Abc_ObjFanin0(minlevelNode)->Id==pNode->Id)
  {
    if(minlevelNode->fCompl0==0)
      {
        if(module==0)
         count0++;
        else
          break;
      }
    else
    {
        if(module==1)
         count1++;
        else
          break;
    }
  }
  if(Abc_ObjFanin1(minlevelNode)->Id==pNode->Id)
  {
    if(minlevelNode->fCompl1==0)
      {
        if(module==0)
         count0++;
        else
          break;
      }
    else
    {
        if(module==1)
         count1++;
        else
          break;
    }
  }
  pNode = Abc_NtkObj(pNtk,Id);
  }
    if((count0>(Abc_NtkLevel(pNtk)/3))&&module==0)
      return 1;  
    if((count0<=(Abc_NtkLevel(pNtk)/3))&&module==0)
      return 0;
    if((count1>(Abc_NtkLevel(pNtk)/3))&&module==1)
      return 0;
    if((count1<=(Abc_NtkLevel(pNtk)/3))&&module==1)
      return 1;  
   return 0;
}
/*------------------------------------------------------------------------*/
/* 获取当前电路level下的平均距离PO距离                                    */
/*------------------------------------------------------------------------*/

int GetAverageLevel(Abc_Ntk_t * pNtk, int level)
{
  Abc_Obj_t* pNode;
  int i = 0, num = 0, sum = 0;
  int average = 0,cont = 0;
    Abc_NtkForEachNode1( pNtk, pNode, i )
    {
      if(Abc_ObjFanoutNum(pNode)>1&& pNode->Level==level)
      {
      num = traceNodetoPo(pNtk,pNode);
      sum = num+sum;
      cont++;
      }
    }
    //若当前节点没有多扇出节点，则返回0，防止sum/0报错
  if(count==0)
     return 0;
  else if(sum == 0)
     return 0;
  else average = sum/cont;
  return average;
}
/*------------------------------------------------------------------------*/
/* 获取当前电路level下的优化效果最好的电路                                    */
/*------------------------------------------------------------------------*/
Abc_Ntk_t* GetBestNtk(Abc_Ntk_t * pNtk,Abc_Ntk_t * pNtk_org, int average,int j,double Lastrate)
{
  Abc_Obj_t *pNode = NULL;
  Abc_Ntk_t *pNtk_best = NULL;
  Abc_Ntk_t *pMiter = NULL,*pNtkRes = NULL,*pNtk_temp = pNtk,*pNtk_copy;
  int fanoutnum = 0, i = 0, NodeNum = 0,level = 0,tempNode0 = 0,tempNode1 = 0,timecount = 0;
  double rate = 0,weight = 0, temp = 0, bestrate = 0;
  pNtk_temp = Ntk_Copy(pNtk);
  Abc_NtkForEachNode( pNtk = Ntk_Copy(pNtk_temp), pNode, i )
  {
    // 获取到当前节点到PO的最短距离
     level = traceNodetoPo(pNtk,pNode);
    if(Abc_ObjFanoutNum(pNode) > 1 && pNode->Level == j && level>average)
    {
/*    tempNode0用来计算当前电路相较于原始电路优化了多少节点，
      tempNode1用来保存上一次优化的节点个数，用来判断当前level下已经无法继续优化下去 */
      tempNode0 = Abc_NtkNodeNum(pNtk);
      tempNode1 = NodeNum;
      pMiter = NULL;
      pNtkRes = NULL;
      Abc_NtkPrintStats( pNtk, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 );
      fanoutnum = pNode->vFanouts.nSize;
      //进行常量复制替换
      AppNtksplit(pNtk, pNode,fanoutnum); 
      //电路优化
      pNtk = Abc_NtkRestrash(pNtk,1);
      pNtk = Optimaize_resyn2(pNtk); 
      Abc_NtkPrintStats( pNtk, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 );
      //构建miter电路计算错误率
      pMiter = Abc_AigCombine2Miter(pNtk, pNtk_org);
      pNtkRes = Abc_NtkCollapse( pMiter, ABC_INFINITY, 0, 1, 0, 0, 0 );
      ErrorRate(pNtkRes,NULL,&rate);
      Abc_NtkDelete(pMiter);
      Abc_NtkDelete(pNtkRes);
      printf("error rate: %lf\n",rate);
      //NodeNum的值为当前电路同之前电路优化了多少个节点
      NodeNum =tempNode0-Abc_NtkNodeNum(pNtk);
      //判断常量替换后错误率同上一次的错误率是否相同且，并且判断电路节点是否减少，若是则返回该电路
      if((rate-Lastrate)==0&&NodeNum>0)
      {
         return pNtk;
      }
      //判断当前电路是否无法再进行电路优化，若是则退出循环
      if(tempNode1==NodeNum)
      {
        return pNtk_best;
      }
      //如果优化后的错误率比上一次优化过程要好，则返回本次优化电路
      if(rate-Lastrate<0)
      {
        return pNtk;
      }
      //判断当前错误率是否大于设定值，并且设定当前循环执行次数，若第一次执行就超过设定错误率，则直接返回当前电路
      if(rate>0.2)
      {
        if(timecount==0)
        {
          return pNtk;
        }
        break;
      }
      //weight为权重值，用来作为我们选取最合适电路的衡量标准，它是用优化节点量除以本次优化与上一次优化的差值来计算得出
      weight = NodeNum/(rate-Lastrate);
      printf("weight: %lf\n",weight);
      if(weight>temp)
      {
        temp = weight;
        pNtk_best = pNtk;
        bestrate = rate;
      }
      timecount++;
    }
  }
  return pNtk_best;
}

/*------------------------------------------------------------------------*/
/* 获取错误率                                                            */
/*------------------------------------------------------------------------*/

double GetErrorRate(Abc_Ntk_t * pNtk_app,Abc_Ntk_t * pNtk_org)
{
  Abc_Ntk_t *pMiter = NULL,*pNtkRes = NULL;
  double rate = 0;
  pMiter = Abc_AigCombine2Miter(pNtk_app, pNtk_org);
  pNtkRes = Abc_NtkCollapse( pMiter, ABC_INFINITY, 0, 1, 0, 0, 0 );
  ErrorRate(pNtkRes,NULL,&rate);
  Abc_NtkDelete(pMiter);
  Abc_NtkDelete(pNtkRes);
  return rate;
}
/*------------------------------------------------------------------------*/
/* 创建新的节点                                                           */
/*------------------------------------------------------------------------*/
Abc_Obj_t * AppNtkCreateObj( Abc_Ntk_t * pNtk,Abc_ObjType_t Type )
{
    Abc_Obj_t * pObj;
    // create new object, assign ID, and add to the array
    pObj = Abc_ObjAlloc( pNtk, Type );
    pObj->Id = pNtk->vObjs->nSize;
    Vec_PtrPush( pNtk->vObjs, pObj );
    pNtk->nObjs++;
   //pObj->iTemp = Vec_PtrSize(pNtk->vCis);
   switch (Type)
   {
    case ABC_OBJ_PI:
        Vec_PtrPush( pNtk->vPis, pObj );
        Vec_PtrPush( pNtk->vCis, pObj );
        break;
    case ABC_OBJ_CONST1: 
        assert(0); 
        break;
    case ABC_OBJ_NODE: 
        break;
    default:
        assert(0); 
        break;
   }
   return pObj;
}


/*------------------------------------------------------------------------*/
/* 释放Mffc存储节点及根节点内存                                           */
/*------------------------------------------------------------------------*/
static void free_MffcArray(AppManager *dMan) {
	int i = 0;
	for(i = 0;i<dMan->nNodes;i++) {
        if(dMan->MffcArray[i] == NULL){continue;}
	}
	ABC_FREE(dMan->MffcArray);
}
/*------------------------------------------------------------------------*/
/* 释放近似电路的内存                                                   */
/*------------------------------------------------------------------------*/
extern void App_Free(AppManager *dMan) 
{
	//free_stackusd(dMan);
  ABC_FREE(dMan->MffcNodes);
  ABC_FREE(dMan->MffcCone);
  ABC_FREE(dMan->MffcNodes);
	free(dMan);
  count=0;
}

